﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BasePlatformCommon;

namespace BasePlatformNet
{
    public abstract class ArtPacket
    {
        // OpCode - Art命令
        public const UInt16 OpPoll = 0x2000;
        public const UInt16 OpPollReply = 0x2100;
        public const UInt16 OpOutput = 0x5000;
        public const UInt16 OpTodRequest = 0x8000;
        public const UInt16 OpTodData = 0x8100;
        public const UInt16 OpTodControl = 0x8200;
        public const UInt16 OpRDM = 0x8300;
        public const UInt16 OpRDMSub = 0x8400;

        // DpCode - 优先级
        public const byte DpLow = 0x10;
        public const byte DpMed = 0x40;
        public const byte DpHigh = 0x80;
        public const byte DpCritical = 0xe0;
        public const byte DpVolatile = 0xf0;

        public UInt16 OpCode;

        protected static byte ArtRevisionVal = 14;

        protected static byte[] ArtID = { (byte)'A', (byte)'r', (byte)'t', (byte)'-', (byte)'N', (byte)'e', (byte)'t', 0 };

        public abstract byte[] generatePacket();

        public abstract Dictionary<string,string> generateDetailDict();
        
        public static ArtPacket decodePacket(byte[] packet)
        {
            if (!CheckValid(packet)) return null;
            UInt16 OpCode = Tools.BytesTo16(packet[9], packet[8]);
            switch (OpCode)
            { 
                case OpPoll:
                    return ArtPoll.decodePacket(packet);
                case OpPollReply:
                    return ArtPollReply.decodePacket(packet);
                case OpOutput:
                    return ArtDMX.decodePacket(packet);
                case OpTodRequest:
                    return ArtTodRequest.decodePacket(packet);
                case OpTodData:
                    return ArtTodData.decodePacket(packet);
                case OpTodControl:
                    return ArtTodControl.decodePacket(packet);
                case OpRDM:
                    return ArtRDM.decodePacket(packet);
                case OpRDMSub:
                    return ArtRDMSub.decodePacket(packet);
                default:
                    return null;
            }
        }

        protected static bool CheckValid(byte[] packet)
        {
            bool result = true;

            for (int i = 0; i < 8; i++)
            {
                if (packet[i] != ArtID[i])
                {
                    result = false;
                    break;
                }
            }
            
            return result;
        }
        
    }

    public class ArtPoll : ArtPacket
    {
        public bool autoReply = true;  // 让ArtNet设备在变动时自动发送ArtPollReply
        public bool diagnostics = false; // 是否接受诊断信息
        public bool diagnosticsUincast = false; // 诊断信息是否广播
        public bool VLC = false; // 是否使用VLC(Visible Light Communication,可见光传输)

        public byte Priority = DpLow;

        public ArtPoll()
        {
            OpCode = OpPoll;
        }

        public override byte[] generatePacket()
        {
            // ID[8] - 8byte
            byte[] packet = new byte[14]; // ArtPoll包长度
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCpde - 2byte
            packet[8] = (byte)(OpPoll & 0xff); // 低位先传
            packet[9] = (byte)((OpPoll >> 8) & 0xff); 

            // ProtVer - 2byte
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // TalktoMe - 1byte, bit7-5 - UnUsed, bit4 - VLC, bit3 - diagnosticsBroadcast, bit2 - diagnostics, bit1 - autoReply, bit0 - Deprecated
            packet[12] = 0;
            if (autoReply) packet[12] |= (1 << 1);
            if (diagnostics) packet[12] |= (1 << 2);
            if (diagnostics && diagnosticsUincast) packet[12] |= (1 << 3);
            if (VLC) packet[12] |= (1 << 4);

            // Priority - 仅用于诊断信息，目前不用
            packet[13] = Priority;

            return packet;
        }

        public override Dictionary<string, string> generateDetailDict()
        {
            Dictionary<string, string> detail = new Dictionary<string, string>();
            detail.Add("AutoReply", autoReply.ToString());
            detail.Add("ReceiveDiagnostics", diagnostics.ToString());
            if (diagnostics)
            {
                detail.Add("DiagnosticsUincast", diagnosticsUincast.ToString());
            }
            detail.Add("VLC", VLC.ToString());

            return detail;
        }

        public static new ArtPoll decodePacket(byte[] packet)
        {
            ArtPoll artpacket = new ArtPoll();
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            artpacket.autoReply = !((packet[12] & (1 << 1)) == 0);
            artpacket.diagnostics = !((packet[12] & (1 << 2)) == 0);
            artpacket.diagnosticsUincast = !((packet[12] & (1 << 3)) == 0);
            artpacket.VLC = !((packet[12] * (1 << 4)) == 0);

            artpacket.Priority = packet[13];

            return artpacket;
        }
    }

    public class ArtPollReply : ArtPacket
    {
        public enum IndicatorState
        { 
            Unknown = 0,
            Locate = 1,
            Mute = 2,
            Normal = 3
        }

        public enum ArtAddrType
        { 
            Unknown = 0,  // 未知的ArtNet地址设置方式
            ByMachine = 1,// 通过设备前面板等方式统一由设备设置
            ByNetwork = 2 // 通过网络统一设置
        }

        public enum ArtStyle
        {
            StNode = 0,
            StController = 1,
            StMedia = 2,
            StRoute = 3,
            StBackup = 4,
            StConfig = 5,
            StVisual = 6

        }
        public byte[] ip;
        public byte[] mac;
        public byte[] bindip;
        public UInt16 port = 0x1936;
        public byte ArtAddrNet; // ArtNet Port-Address的Net部分
        public byte ArtAddress; // ArtNet Port-Address的subnet部分，后四位以0000代替

        public UInt16 OEM;

        public byte style = 0;
        public bool ArtAddr_2byte = true; // true - 设备支持2byte的Port-Address(ArtNet3/4), false - 设备支持1byte的Port-Address(ArtNet2)

        public int bindIndex = 0;

        public bool UBEAState = false;
        public byte UBEAVersion = 0;
        public byte indicator = 3;
        public byte addrAuthority = 2; // 设备ArtNetAddress编址模式
        public bool BootMode = false; // 设备boot模式：false -  Normal firmware boot (from flash), true -  Booted from ROM
        public bool RDMSupport = true; // 设备是否支持RMD，默认支持
        public bool WebConfig = false; // 设备是否支持通过浏览器配置
        public bool DHCPConfig = false; // 设备是否通过DHCP配置IP
        public bool DHCPCapable = false; // 设备是否支持DHCP
        public bool sACNCapale = false; // 设备是否支持在Art-Net与sACN间切换
        public bool squawking = false;

        public UInt16 ESTACode = 0x0000; // ESTA码
        public UInt16 VersionInfo;

        public string shortName;  // 设备短名称
        public string longName;   // 设备长名称
        public string nodeReport; // 设备报告
        public byte[] nodeReportArr; // 设备报告数组（目前用于分控信息显示）

        public int channelNum;                // 设备端口数 - 上限为4
        public ChannelState[] channelStates;  // 端口状态
        public ArtInputState[] inputStates;   // 端口输入状态
        public ArtOutputState[] outputStates; // 端口输出状态
        public byte[] inputAddr;              // 输入端口ArtNet Port-Address Universe地址
        public byte[] outputAddr;             // 输出端口ArtNet Port-Address Universe地址

        public byte Macro = 0;  // 每个bit代表一个Macro Trigger是否被激活
        public byte Remote = 0; // 每个bit代表一个Remote Trigger是否被激活

        public ArtPollReply()
        {
            OpCode = OpPollReply;
            ip = new byte[4];
            mac = new byte[6];
            bindip = new byte[4];
            channelStates = new ChannelState[4];
            inputStates = new ArtInputState[4];
            outputStates = new ArtOutputState[4];
            inputAddr = new byte[4];
            outputAddr = new byte[4];
        }

        public override Dictionary<string, string> generateDetailDict()
        {
            Dictionary<string, string> detail = new Dictionary<string, string>();

            detail.Add("IP",String.Format("{0}.{1}.{2}.{3}",ip[0],ip[1],ip[2],ip[3]));
            detail.Add("Port", port.ToString());
            detail.Add("Version", VersionInfo.ToString());
            detail.Add("Net", ArtAddrNet.ToString());
            detail.Add("Sub", ((ArtAddress >> 4) & 0x0f).ToString());
            detail.Add("OEM", "0x"+OEM.ToString("X4"));
            detail.Add("UBEA Version", UBEAVersion.ToString());
            detail.Add("UBEA Present", UBEAState.ToString());
            detail.Add("RDM Support", RDMSupport.ToString());
            detail.Add("ROM Booted", BootMode.ToString());
            detail.Add("Address Authority", String.Format("{0}(0x{1:X})",Enum.GetName(typeof(ArtAddrType), (ArtAddrType)addrAuthority),addrAuthority));
            detail.Add("Indicator", String.Format("{0}(0x{1:X2})",Enum.GetName(typeof(IndicatorState), (IndicatorState)indicator),indicator));
            detail.Add("ESTA Code", "0x"+ESTACode.ToString("X4"));
            detail.Add("Short Name", shortName);
            detail.Add("Long Name", longName);
            detail.Add("Node Report", nodeReport);
            detail.Add("Port Number", channelNum.ToString());
            for (int i = 0; i < channelNum; i++)
            {
                detail.Add(String.Format("Port Type[{0}]", i), String.Format("{0}(0x{1:X})", Enum.GetName(typeof(ChannelState.DataType), (ChannelState.DataType)channelStates[i].datatype), channelStates[i].datatype));
            }
            for (int i = 0; i < channelNum; i++)
            { 
                detail.Add(String.Format("Input State[{0}]",i), "0x"+inputStates[i].generatebyte().ToString("X2"));
            }
            for (int i = 0; i < channelNum; i++)
            {
                detail.Add(String.Format("Output State[{0}]", i), "0x" + outputStates[i].generatebyte().ToString("X2"));
            }
            for (int i = 0; i < channelNum; i++)
            {
                detail.Add(String.Format("Input Subswitch[{0}]", i), String.Format("0x{0:X}[{1}]", inputAddr[i], ((ArtAddrNet << 8) | ArtAddress | inputAddr[i])));
            }
            for (int i = 0; i < channelNum; i++)
            {
                detail.Add(String.Format("Output Subswitch[{0}]", i), String.Format("0x{0:X}[{1}]", outputAddr[i], ((ArtAddrNet << 8) | ArtAddress | outputAddr[i])));
            }
            detail.Add("SwVideo", "0");
            detail.Add("SwRemote", "0x"+Remote.ToString("X2"));
            detail.Add("SwMacro", "0x" + Macro.ToString("X2"));
            detail.Add("Style", String.Format("{0}(0x{1:X2})", Enum.GetName(typeof(ArtStyle), (ArtStyle)style), style));
            detail.Add("MAC", Tools.Byte2HexString(mac).Replace("\n", ""));
            detail.Add("BindIP", Tools.Byte2HexString(bindip).Replace("\n", ""));
            detail.Add("BindIndex", bindIndex.ToString());
            detail.Add("WebConf Avilable", WebConfig.ToString());
            detail.Add("DHCP Avilable", DHCPCapable.ToString());
            if (DHCPCapable)
            {
                detail.Add("DHCP Active", DHCPConfig.ToString());
            }
            if (ArtAddr_2byte)
            {
                detail.Add("Port Address Type", "15-bit Addr");
            }
            else
            {
                detail.Add("Port Address Type", "8-bit Addr");
            }
            return detail;
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[239];

            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }
            
            // OpCode - 2byte:8
            packet[8] = (byte)(OpPollReply & 0xff);
            packet[9] = (byte)((OpPollReply >> 8) & 0xff);

            // IP - 4byte:10
            for (int i = 0; i < 4; i++)
            {
                packet[10 + i] = ip[i];
            }

            // Port - 2byte:14
            packet[14] = (byte)(port & 0xff);
            packet[15] = (byte)((port >> 8) & 0xff);

            // VersInfo - 2byte:16
            packet[16] = (byte)((VersionInfo >> 8) & 0xff);
            packet[17] = (byte)(VersionInfo & 0xff);

            // NetSwitch - 1byte:18
            packet[18] = ArtAddrNet;

            // SubSwitch - 1byte:19
            packet[19] = (byte)((ArtAddress >> 4) & 0x0f);

            // OEM - 2yte:20
            packet[20] = (byte)((OEM >> 8) & 0xff);
            packet[21] = (byte)(OEM & 0xff);

            // UBEA Version - 1byte:22
            packet[22] = UBEAVersion;

            // Status1 - 1byte:23
            byte status1 = 0;
            if (UBEAState) status1 |= 1;
            if (RDMSupport) status1 |= (1 << 1);
            if (BootMode) status1 |= (1 << 2);
            status1 |= (byte)(addrAuthority << 4);
            status1 |= (byte)(indicator << 6);
            packet[23] = status1;

            // ESTA manufacturer code - 2byte:24
            packet[24] = (byte)((ESTACode) & 0xff);
            packet[25] = (byte)((ESTACode >> 8) & 0xff);

            // Short Name - 18byte:26
            for (int i = 0; i < 18; i++)
            {
                if (i < shortName.Length)
                {
                    packet[26 + i] = (byte)shortName[i];
                }
                else
                {
                    packet[26 + i] = 0;
                }
            }

            // Long Name - 64byte:44
            for (int i = 0; i < 64; i++)
            {
                if (i < longName.Length)
                {
                    packet[44 + i] = (byte)longName[i];
                }
                else
                {
                    packet[44 + i] = 0;
                }
            }

            // Node Report - 64byte:108
            for (int i = 0; i < 64; i++)
            {
                if (i < nodeReport.Length)
                {
                    packet[108 + i] = (byte)nodeReport[i];
                }
                else
                {
                    packet[108 + i] = 0;
                }
            }

            // NumPotrs - 2byte:172
            packet[172] = 0;
            packet[173] = (byte)channelNum;

            // PortType - 4byte:174
            for (int i = 0; i < 4; i++)
            {
                packet[174 + i] = channelStates[i].generatebyte();
            }

            // GoodInput - 4byte:178
            for (int i = 0; i < 4; i++)
            {
                packet[178 + i] = inputStates[i].generatebyte();
            }

            // GoodOutput - 4byte:182
            for (int i = 0; i < 4; i++)
            {
                packet[182 + i] = outputStates[i].generatebyte();
            }

            // SwIn - 4byte:186
            for (int i = 0; i < 4; i++)
            {
                packet[186 + i] = inputAddr[i];
            }

            // SwOut - 4byte:190
            for (int i = 0; i < 4; i++)
            {
                packet[190 + i] = outputAddr[i];
            }

            // SwVideo - 1byte:194
            packet[194] = 0;

            // SwMarco - 1byte:195
            packet[195] = Macro;

            // SwRemote - 1byte:196
            packet[196] = Remote;

            // Spare - 3byte:197
            packet[197] = 0;
            packet[198] = 0;
            packet[199] = 0;

            // Style - 1byte:200
            packet[200] = style;

            // MAC - 6byte:201
            for (int i = 0; i < 6; i++)
            {
                packet[201 + i] = mac[i];
            }

            // BindIP - 4byte:207
            for (int i = 0; i < 4; i++)
            {
                packet[207 + i] = bindip[i];
            }

            // BindIndex - 1byte:211
            packet[211] = (byte)bindIndex;

            // Status2 - 1byte:212
            byte status2 = 0;
            if (WebConfig) status2 |= 1;
            if (DHCPConfig) status2 |= (1 << 1);
            if (DHCPCapable) status2 |= (1 << 2);
            if (ArtAddr_2byte) status2 |= (1 << 3);
            if (sACNCapale) status2 |= (1 << 4);
            if (squawking) status2 |= (1 << 5);
            packet[212] = status2;

            // Filler - 26bytes:213
            for (int i = 0; i < 26; i++)
            {
                packet[213 + i] = 0;
            }

            return packet;
        }

        public static new ArtPollReply decodePacket(byte[] packet)
        {
            ArtPollReply artpacket = new ArtPollReply();
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            for (int i = 0; i < 4; i++)
            {
                artpacket.ip[i] = packet[10 + i];
                artpacket.channelStates[i] = new ChannelState(packet[174 + i]);
                artpacket.inputStates[i] = new ArtInputState(packet[178 + i]);
                artpacket.outputStates[i] = new ArtOutputState(packet[182 + i]);

                artpacket.inputAddr[i] = packet[186 + i];
                artpacket.outputAddr[i] = packet[190 + i];

                artpacket.bindip[i] = packet[207 + i];
            }
            for (int i = 0; i < 6; i++)
            {
                artpacket.mac[i] = packet[201 + i];
            }

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 18; i++)
            {
                if (packet[26 + i] != 0)
                {
                    sb.Append((char)(packet[26 + i]));
                }
                else
                {
                    break;
                }

            }
            artpacket.shortName = sb.ToString();
            sb.Clear();
            for (int i = 0; i < 64; i++)
            {
                if (packet[44 + i] != 0)
                {
                    sb.Append((char)(packet[44 + i]));
                }
                else
                {
                    break;
                }
            }
            artpacket.longName = sb.ToString();
            sb.Clear();
            for (int i = 0; i < 64; i++)
            {
                if (packet[108 + i] != 0)
                {
                    sb.Append((char)(packet[108 + i]));
                }
                else
                {
                    break;
                }
            }
            artpacket.nodeReport = sb.ToString();
            artpacket.nodeReportArr = new byte[64];
            Array.Copy(packet, 108, artpacket.nodeReportArr, 0, 64);
            sb.Clear();
            artpacket.port = (UInt16)((packet[15] << 8) + packet[14]);
            artpacket.ArtAddrNet = packet[18];
            artpacket.ArtAddress = (byte)((packet[19] << 4) & 0xf0);
            artpacket.OEM = (UInt16)((packet[20] << 8) + packet[21]);
            artpacket.UBEAVersion = packet[22];
            artpacket.indicator = (byte)((packet[23] >> 6) & 0x03);
            artpacket.addrAuthority = (byte)((packet[23] >> 4) & 0x03);
            artpacket.UBEAState = !((packet[23] & 1) == 0);
            artpacket.RDMSupport = !((packet[23] &(1 << 1)) == 0);
            artpacket.BootMode = !((packet[23] & (1 << 2)) == 0);
            artpacket.ESTACode = (UInt16)((packet[25] << 8) + packet[24]);
            artpacket.channelNum = packet[173];
            artpacket.Macro = packet[195];
            artpacket.Remote = packet[196];
            artpacket.style = packet[200];
            artpacket.bindIndex = packet[211];
            artpacket.WebConfig = !((packet[212] & 1) == 0);
            artpacket.DHCPConfig = !((packet[212] & (1 << 1)) == 0);
            artpacket.DHCPCapable = !((packet[212] & (1 << 2)) == 0);
            artpacket.ArtAddr_2byte = !((packet[212] & (1 << 3)) == 0);
            artpacket.sACNCapale = !((packet[212] & (1 << 4)) == 0);
            artpacket.squawking = !((packet[212] & (1 << 5)) == 0);

            return artpacket;
        }
    }

    public class ArtDMX : ArtPacket
    {
        //UInt16 ArtAddr;
        public byte Net;
        public byte Address;
        public byte PhysicalPort;
        public int sequence;
        public int datalen;
        public byte[] data;

        public ArtDMX(int len = 0)
        {
            if (len != 0)
            {
                datalen = len;
                data = new byte[len];
            }
            else
            {
                datalen = 0;
                data = null;
            }
        }

        public override Dictionary<string, string> generateDetailDict()
        {
            Dictionary<string, string> detail = new Dictionary<string, string>();
            for (int i = 0; i < 32; i++)
            {
                string DMXRow = "";
                for (int j = 0; j < 16; j++)
                {
                    string dmxbyte = data[i * 16 + j].ToString("X2");
                    DMXRow += dmxbyte;
                    if (j != 15) DMXRow += " ";
                    if (j == 7) DMXRow += " ";
                }
                detail.Add(String.Format("0x{0}1", i.ToString("X2")), DMXRow);
            }

            return detail;
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[datalen + 18];

            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpOutput & 0xff);
            packet[9] = (byte)((OpOutput >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // Sequence - 1byte:12
            packet[12] = (byte)(sequence % 256);

            // Physical - 1byte:13
            packet[13] = PhysicalPort;

            // PortAddress - 2byte:14
            packet[14] = Address;
            packet[15] = Net;

            // Length - 2byte:16
            packet[16] = (byte)((datalen >> 8) & 0xff);
            packet[17] = (byte)(datalen & 0xff);

            // Data - (len)byte:18
            for (int i = 0; i < datalen; i++)
            {
                packet[18 + i] = data[i];
            }

            return packet;
        }

        public static new ArtDMX decodePacket(byte[] packet)
        {
            int len = (packet[16] << 8) + packet[17];
            ArtDMX artpacket = new ArtDMX(len);
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.sequence = packet[12];
            artpacket.PhysicalPort = packet[13];
            artpacket.Address = packet[14];
            artpacket.Net = packet[15];

            for (int i = 0; i < len; i++)
            {
                artpacket.data[i] = packet[18 + i];
            }

            return artpacket;
        }
    }

    public class ArtTodRequest : ArtPacket
    {
        public byte Net;
        public byte Command = 0;
        public int AddrCount;
        public byte[] Addr;

        public ArtTodRequest()
        {
            Addr = new byte[32];
        }

        public override Dictionary<string, string> generateDetailDict()
        {
            throw new NotImplementedException();
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[56];
            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpTodRequest & 0xff);
            packet[9] = (byte)((OpTodRequest >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // Filter - 2byte:12
            packet[12] = 0;
            packet[13] = 0;

            // Spare - 7byte:14
            packet[14] = 0;
            packet[15] = 0;
            packet[16] = 0;
            packet[17] = 0;
            packet[18] = 0;
            packet[19] = 0;
            packet[20] = 0;

            // Net - 1byte:21
            packet[21] = Net;

            // Command - 1byte:22
            packet[22] = Command;

            //AddDount - 1byte:23
            if ((0 <= AddrCount) && (AddrCount <= 32))
            {
                packet[23] = (byte)(AddrCount);
            }
            else
            {
                packet[23] = 32;
            }

            //Address - 32byte:24
            for (int i = 0; i < 32; i++)
            {
                packet[24 + i] = Addr[i];
            }

            return packet;
        }

        public static new ArtTodRequest decodePacket(byte[] packet)
        {
            ArtTodRequest artpacket = new ArtTodRequest();
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.Net = packet[21];
            artpacket.Command = packet[22];
            artpacket.AddrCount = packet[23];
            for (int i = 0; i < 32; i++)
            { 
                artpacket.Addr[i] = packet[24+i];
            }

            return artpacket;
        }
    }

    public class ArtTodControl : ArtPacket
    {
        public byte Net;
        public byte Command;
        public byte Address;

        public override Dictionary<string, string> generateDetailDict()
        {
            throw new NotImplementedException();
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[24];
            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpTodControl & 0xff);
            packet[9] = (byte)((OpTodControl >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // Filter - 2byte:12
            packet[12] = 0;
            packet[13] = 0;

            // Spare - 7byte:14
            packet[14] = 0;
            packet[15] = 0;
            packet[16] = 0;
            packet[17] = 0;
            packet[18] = 0;
            packet[19] = 0;
            packet[20] = 0;

            // Net - 1byte:21
            packet[21] = Net;

            // Command - 1byte:22
            packet[22] = Command;

            // Address - 1byte:23
            packet[23] = Address;

            return packet;
        }

        public static new ArtTodControl decodePacket(byte[] packet)
        {
            ArtTodControl artpacket = new ArtTodControl();
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.Address = packet[23];
            artpacket.Command = packet[22];
            artpacket.Net = packet[21];

            return artpacket;
        }
    }

    public class ArtTodData : ArtPacket
    {
        public byte RDMVersion = 1;
        public byte PhyPort;
        public int BindIndex;
        public byte Net;
        public byte CommandResponse;
        public byte Address;
        public int UIDTotal;
        public int BlockCount;
        public int UIDCount;
        public byte[] ToD;

        public int universe
        {
            get
            {
                return Net << 8 | Address;
            }
        }

        public ArtTodData(int uidCount = 0)
        {
            if (uidCount != 0)
            {
                this.UIDCount = uidCount;
                ToD = new byte[6 * uidCount];
            }
            else
            {
                this.UIDCount = 0;
                ToD = null;
            }

        }

        public override Dictionary<string, string> generateDetailDict()
        {
            throw new NotImplementedException();
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[6 * UIDCount + 28];
            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpTodData & 0xff);
            packet[9] = (byte)((OpTodData >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // RDMVer - 1byte:12
            packet[12] = RDMVersion;

            // Port - 1byte:13
            packet[13] = PhyPort;

            // Spare - 6byte:14
            packet[14] = 0;
            packet[15] = 0;
            packet[16] = 0;
            packet[17] = 0;
            packet[18] = 0;
            packet[19] = 0;

            // BindIndex - 1byte:20
            packet[20] = (byte)BindIndex;

            // Net - 1byte:21
            packet[21] = Net;

            // CommandResponse - 1byte:22
            packet[22] = CommandResponse;

            // Address - 1byte:23
            packet[23] = Address;

            // UidTotal - 2byte:24
            packet[24] = (byte)((UIDTotal >> 8) & 0xff);
            packet[25] = (byte)(UIDTotal & 0xff);

            // BolckCount - 1byte:26
            packet[26] = (byte)(BlockCount);

            // UidCount - 1byte:27
            packet[27] = (byte)UIDCount;

            //Tod - (6*UidCount)byte:28
            for (int i = 0; i < 6 * UIDCount; i++)
            {
                packet[28 + i] = ToD[i];
            }

            return packet;
        }

        public static new ArtTodData decodePacket(byte[] packet)
        {
            int uidcount = packet[27];
            ArtTodData artpacket = new ArtTodData(uidcount);
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.RDMVersion = packet[12];
            artpacket.PhyPort = packet[13];
            artpacket.BindIndex = packet[20];
            artpacket.Net = packet[21];
            artpacket.CommandResponse = packet[22];
            artpacket.Address = packet[23];
            artpacket.UIDTotal = (packet[24] << 8) + packet[25];
            artpacket.BlockCount = packet[26];
            artpacket.UIDCount = uidcount;
            for (int i = 0; i < 6 * uidcount; i++)
            {
                artpacket.ToD[i] = packet[28 + i];
            }

            return artpacket;
        }

    }

    public class ArtRDM : ArtPacket
    {
        public byte RDMVersion = 1;
        public byte Net;
        public byte Command = 0;
        public byte Address;
        public int RDMlen;
        public byte[] RDMData;

        public ArtRDM(int len)
        {
            this.RDMlen = len;
            RDMData = new byte[len + 2];
        }

        public override Dictionary<string, string> generateDetailDict()
        {
            throw new NotImplementedException();
        }

        public ArtRDM(byte[] Data)
        {
            int len = Data[2];
            if (len >= 24)
            {
                this.RDMlen = len;
                this.RDMData = new byte[len + 2];
                Tools.CopyByte(ref RDMData, Data, len + 2);
            }
            else
            {
                throw (new Exception("Not a valid RDM Data"));
            }
        }

        public override byte[] generatePacket()
        {
            byte[] packet = new byte[RDMlen + 1 + 24];
            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpRDM & 0xff);
            packet[9] = (byte)((OpRDM >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // RDMVer - 1byte:12
            packet[12] = RDMVersion;

            // Filter - 1byte:13
            packet[13] = 0;

            // Spare - 7byte:14
            packet[14] = 0;
            packet[15] = 0;
            packet[16] = 0;
            packet[17] = 0;
            packet[18] = 0;
            packet[19] = 0;
            packet[20] = 0;

            // Net - 1byte:21
            packet[21] = Net;

            // CommandResponse - 1byte:22
            packet[22] = Command;

            // Address - 1byte:23
            packet[23] = Address;

            // RDMPaacket - (RDMlen+2)byte:24
            for (int i = 0; i < RDMlen + 1; i++)
            {
                packet[24 + i] = RDMData[i + 1]; //跳过RDM包第一位
            }

            return packet;
        }

        public static new ArtRDM decodePacket(byte[] packet)
        {
            int len = packet[25];
            ArtRDM artpacket = new ArtRDM(len);
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.RDMVersion = packet[12];
            artpacket.Net = packet[21];
            artpacket.Command = packet[22];
            artpacket.Address = packet[23];
            artpacket.RDMlen = len;
            artpacket.RDMData[0] = RDMCore.SC_RDM;
            for (int i = 0; i < len + 1; i++)
            {
                artpacket.RDMData[i + 1] = packet[24 + i];
            }

            return artpacket;
        }
    }

    public class ArtRDMSub : ArtPacket
    {
        public byte RDMVersion;
        public byte[] UID;
        public byte CommandClass;
        public UInt16 ParameterID;
        public UInt16 SubDevice;
        public UInt16 SubCount;
        public UInt16[] Data;

        public override Dictionary<string, string> generateDetailDict()
        {
            throw new NotImplementedException();
        }

        public ArtRDMSub(int subNum = 0)
        {
            UID = new byte[6];
            SubCount = (UInt16)subNum;
            if (subNum != 0)
            {
                Data = new UInt16[subNum];
            }
            else
            {
                Data = null;
            }
        }

        public override byte[] generatePacket()
        {
            byte[] packet;
            if ((CommandClass == RDMCore.SET_COMMAND) || (CommandClass == RDMCore.GET_COMMAND_RESPONSE))
            {
                packet = new byte[32 + SubCount * 2];
            }
            else
            {
                packet = new byte[32];
            }

            // ArtID - 8byte:0
            for (int i = 0; i < 8; i++)
            {
                packet[i] = ArtID[i];
            }

            // OpCode - 2byte:8
            packet[8] = (byte)(OpRDMSub & 0xff);
            packet[9] = (byte)((OpRDMSub >> 8) & 0xff);

            // ProtVer - 2byte:10
            packet[10] = 0;
            packet[11] = ArtRevisionVal;

            // RDMVer - 1byte:12
            packet[12] = RDMVersion;

            // Filter - 1byte:13
            packet[13] = 0;

            // UID - 6byte:14
            for (int i = 0; i < 6; i++)
            {
                packet[14 + i] = UID[i];
            }

            // spare - 1byte:20
            packet[20] = 0;

            // CommandClass - 1byte:21
            packet[21] = CommandClass;

            // ParameterID - 2byte:22
            packet[22] = (byte)((ParameterID >> 8) & 0xff);
            packet[23] = (byte)(ParameterID & 0xff);

            // SubDevice - 2byte:24
            packet[24] = (byte)((SubDevice >> 8) & 0xff);
            packet[25] = (byte)(SubDevice & 0xff);

            // SubCount - 2byte:26
            packet[26] = (byte)((SubCount >> 8) & 0xff);
            packet[27] = (byte)(SubCount & 0xff);

            // spare - 4yte:28
            packet[28] = 0;
            packet[29] = 0;
            packet[30] = 0;
            packet[31] = 0;

            // Data - 0/(SubCount*2)byte:32
            if ((CommandClass == RDMCore.SET_COMMAND) || (CommandClass == RDMCore.GET_COMMAND_RESPONSE))
            {
                for (int i = 0; i < SubCount; i++)
                {
                    packet[32 + 2 * i] = (byte)((Data[i] >> 8) & 0xff);
                    packet[32 + 2 * i + 1] = (byte)(Data[i] & 0xff);
                }
            }

            return packet;
        }

        public static new ArtRDMSub decodePacket(byte[] packet)
        {
            int subNum = (packet[26] << 8)+packet[27];
            ArtRDMSub artpacket = new ArtRDMSub(subNum);
            artpacket.OpCode = Tools.BytesTo16(packet[9], packet[8]);
            
            artpacket.RDMVersion = packet[12];

            for (int i = 0; i < 6; i++)
            {
                artpacket.UID[i] = packet[14 + i];
            }

            artpacket.CommandClass = packet[21];
            artpacket.ParameterID = (UInt16)((packet[22] << 8) + packet[23]);
            artpacket.SubDevice = (UInt16)((packet[24] << 8) + packet[25]);
            artpacket.SubCount = (UInt16)subNum;

            if ((artpacket.CommandClass == RDMCore.SET_COMMAND) || (artpacket.CommandClass == RDMCore.GET_COMMAND_RESPONSE))
            {
                for (int i = 0; i < subNum; i++)
                {
                    artpacket.Data[i] = (byte)((packet[32 + i * 2] << 8) + packet[32 + i * 2 + 1]);
                }
            }

            return artpacket;
        }
    }

}
